package cn.cdu.basics.security.jwt;

import cn.cdu.basics.utils.ResponseUtil;
import cn.cdu.basics.utils.SecurityUtil;
import cn.cdu.basics.baseVo.TokenUser;
import cn.cdu.basics.parameter.LoginProperties;
import cn.cdu.data.utils.NullUtils;
import com.google.gson.Gson;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;


@Slf4j
public class JwtRoleFilter extends BasicAuthenticationFilter {

    private SecurityUtil securityUtil;

    private StringRedisTemplate stringRedisTemplate;

    private LoginProperties loginProperties;

    private static final boolean RESPONSE_FAIL_FLAG = false;

    private static final int RESPONSE_NO_ROLE_CODE = 401;

    @ApiOperation(value = "判断登录是否失效")
    private UsernamePasswordAuthenticationToken getUsernamePasswordAuthenticationToken(String header, HttpServletResponse response) {
        log.info("[JWT] 开始校验 token, header(accessToken): {}", header);
        String userName = null;
        String redisKey = LoginProperties.HTTP_TOKEN_PRE + header;
        String tokenInRedis = stringRedisTemplate.opsForValue().get(redisKey);
        log.info("[JWT] Redis 查找 key: {}, 查到的值: {}", redisKey, tokenInRedis);
        if(NullUtils.isNull(tokenInRedis)){
            log.warn("[JWT] Token 校验失败，Redis 未查到 token，返回未登录");
            log.warn("[JWT] Redis key used: {}", redisKey);
            ResponseUtil.out(response, ResponseUtil.resultMap(RESPONSE_FAIL_FLAG,RESPONSE_NO_ROLE_CODE,"登录状态失效，需要重登！"));
            return null;
        }
        log.info("[JWT] Token found in Redis: {}", tokenInRedis);
        TokenUser tokenUser = new Gson().fromJson(tokenInRedis, TokenUser.class);
        log.info("[JWT] TokenUser 解析结果: {}", tokenUser);
        userName = tokenUser.getUsername();
        log.info("[JWT] Token user: {}", userName);
        List<GrantedAuthority> permissionList = new ArrayList<>();
        if(loginProperties.getSaveRoleFlag()){
            for(String permission : tokenUser.getPermissions()){
                permissionList.add(new SimpleGrantedAuthority(permission));
            }
        } else{
            permissionList = securityUtil.getCurrUserPerms(userName);
        }
        if(!tokenUser.getSaveLogin()){
            log.info("[JWT] 非持久化登录，刷新 token 过期时间");
            stringRedisTemplate.opsForValue().set(LoginProperties.USER_TOKEN_PRE + userName, header, loginProperties.getUserTokenInvalidDays(), TimeUnit.MINUTES);
            stringRedisTemplate.opsForValue().set(LoginProperties.HTTP_TOKEN_PRE + header, tokenInRedis, loginProperties.getUserTokenInvalidDays(), TimeUnit.MINUTES);
        }
        if(!NullUtils.isNull(userName)) {
            User user = new User(userName, "", permissionList);
            log.info("[JWT] Authentication successful for user: {}", userName);
            return new UsernamePasswordAuthenticationToken(user, null, permissionList);
        }
        log.warn("[JWT] No username found in token");
        return null;
    }

   @Override
   @ApiOperation(value = "自定义权限过滤")
   protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
       String tokenHeader = request.getHeader(LoginProperties.HTTP_HEADER);
       if(NullUtils.isNull(tokenHeader)){
           tokenHeader = request.getParameter(LoginProperties.HTTP_HEADER);
       }
       log.info("Received token header: {}", tokenHeader);
       if (NullUtils.isNull(tokenHeader)) {
           log.info("No token header found, skipping authentication");
           chain.doFilter(request, response);
           return;
       }
       try {
           UsernamePasswordAuthenticationToken token = getUsernamePasswordAuthenticationToken(tokenHeader, response);
           if (token != null) {
               SecurityContextHolder.getContext().setAuthentication(token);
               log.info("Authentication successful for user: {}", token.getPrincipal());
           }
       }catch (Exception e){
           log.error("Authentication failed: {}", e.getMessage(), e);
       }
       chain.doFilter(request, response);
   }

    /**
     * 默认类构造器
     */
    public JwtRoleFilter(AuthenticationManager manager, AuthenticationEntryPoint point) {
        super(manager, point);
    }

    public JwtRoleFilter(AuthenticationManager manager, LoginProperties loginProperties, StringRedisTemplate redis, SecurityUtil securityUtil) {
        super(manager);
        this.stringRedisTemplate = redis;
        this.securityUtil = securityUtil;
        this.loginProperties = loginProperties;
    }
}

